
#include "time_mgmt.h"
#include <iostream>

using namespace std;

#define EVENT_MUTEX        L"eventMutex"
#define TIMER_EVENT        L"timerEvent"

void aa();
void bb();


void test()
{
    UINT timerID_1 = timers::instance().createTimer(5000, aa);
    UINT timerID_2 = timers::instance().createTimer(2000, NULL);
    UINT timerID_3 = timers::instance().createTimer(1000, aa);
    UINT timerID_4 = timers::instance().createTimer(800, bb);
    UINT timerID_5 = timers::instance().createTimer(400, bb);
    UINT timerID_6 = timers::instance().createTimer(100, bb);
    Sleep(1);
    cout<<endl<<"timer1: "<<timerID_1<<" stared!"<<endl;
    cout<<endl<<"timer2: "<<timerID_2<<" stared!"<<endl;
    cout<<endl<<"timer6: "<<timerID_6<<" stared!"<<endl;
}


void aa()
{
    cout<<endl<<"TEST aaas"<<endl;
}

void bb()
{
    cout<<endl<<"TEST bb"<<endl;
}


void timoutFct()
{
    timers::instance().updatePeekTimeouts();
}

VOID CALLBACK TimersProc(HWND hwnd, UINT message, UINT idTimerEvent, DWORD dwTime)     
{ 
    timers::timersEnum timerKind = timers::instance().getTimerNo(idTimerEvent);
    VOID (*userDefFct)(VOID) = timers::instance().getTimerDefFct(timerKind);

    userDefFct();
    switch(timerKind) {
        case timers::IDT_TIMER1: 
            cout<<"Timer1"<<endl;                      
            break;
        case timers::IDT_TIMER2:
            cout<<"Timer2"<<endl;
            break;
        case timers::IDT_TIMER3:
            cout<<"Timer3"<<endl;
            break;
        case timers::IDT_TIMER4:
            cout<<"Timer4"<<endl;
            break;
        case timers::IDT_TIMER5:
            cout<<"Timer5"<<endl;
            break;    
        default: {}    
    }  

}

DWORD WINAPI timer_loop(LPVOID lpParam)
{    
    timers::timersEnum *currTimer = (timers::timersEnum*)lpParam;
    UINT timerMsInterval = timers::instance().getTimerMsInterval(*currTimer);
    UINT TimerIdEvent    = SetTimer(NULL, 0, timerMsInterval, (TIMERPROC)TimersProc);
    UINT storeEvtResult  = timers::instance().storeTimerIdEvent(*currTimer, TimerIdEvent);

    if( (timerMsInterval > 0) && (TimerIdEvent != 0) && (storeEvtResult == EXIT_SUCCESS) ) {     
        HANDLE timerEv = OpenEvent(EVENT_MODIFY_STATE, FALSE, TIMER_EVENT);
        SetEvent(timerEv);
    
        cout<<endl<<"msg started!"<<endl;
        MSG msg;          
        while (GetMessage(&msg, NULL, 0, 0)) { 
            TranslateMessage(&msg);  
            DispatchMessage(&msg);  
        }
        cout<<endl<<"msg ended!"<<endl;

        return EXIT_SUCCESS;
    }
    else
        KillTimer(NULL, TimerIdEvent);
    
    return EXIT_FAILURE;
}

HANDLE timers::evMutex = CreateMutex(NULL, FALSE, EVENT_MUTEX);

UINT timers::createTimer(const UINT ms_interval, VOID (*ext_fct)(VOID) )
{
    UINT fresult = IDT_UNDEF;

    timersEnum new_timer = getFreeTimer();
    if(new_timer != IDT_UNDEF) {   
        if(WaitForSingleObject(evMutex, 100) == WAIT_OBJECT_0) {          
            DWORD  ThreadID = 0;
            HANDLE timEvent = CreateEvent( NULL, FALSE, FALSE, TIMER_EVENT);  

            HANDLE hThread  = CreateThread(NULL, 0, timer_loop, (LPVOID*)&new_timer, 0, &ThreadID);
            setTaken(new_timer, IDT_KTIMER, 0, ThreadID, hThread, ext_fct, ms_interval);

            if( WAIT_OBJECT_0 == WaitForSingleObject(timEvent, 10) )
                fresult = new_timer;
            else {
                cout<<endl<<"<!> emergency thread closing!"<<endl;
                WaitForSingleObject(hThread, INFINITE);
                releaseTaken(new_timer);
                ResetEvent(timEvent);
            }

            ReleaseMutex(evMutex);
        }   
    }

    return fresult;
}

UINT timers::createPeekTimeout(const UINT timeout)
{
    UINT fresult = IDT_UNDEF;

    timersEnum new_timeout = getFreeTimer();
       
    if(new_timeout != IDT_UNDEF) {   
        if( isTimeoutDeployed() == FALSE) {
            if(WaitForSingleObject(evMutex, 50) == WAIT_OBJECT_0) {          
                DWORD  ThreadID = 0;
                HANDLE timEvent = CreateEvent( NULL, FALSE, FALSE, TIMER_EVENT);  
                HANDLE hThread  = CreateThread(NULL, 0, timer_loop, (LPVOID*)&new_timeout, 0, &ThreadID);

                setTaken(new_timeout, IDT_KTIMEOUT, timeout, ThreadID, hThread, timoutFct, 1);
                if( WAIT_OBJECT_0 == WaitForSingleObject(timEvent, 10) )
                    fresult = new_timeout;
                else {
                    cout<<endl<<"<!> emergency thread closing!"<<endl;
                    WaitForSingleObject(hThread, INFINITE);
                    releaseTaken(new_timeout);
                    ResetEvent(timEvent);
                }

                ReleaseMutex(evMutex);
            }
        }
        else {
            setTaken(new_timeout, IDT_KTIMEOUT, timeout, 0, NULL, timoutFct, 1);
            fresult = new_timeout;
        }
    }


    return fresult;
}

